package com.maezia.tdr.common;

import cn.hutool.http.Header;
import com.alibaba.fastjson.JSONObject;
import com.maezia.tdr.common.enums.StatusCodeEnum;
import com.maezia.tdr.common.exception.TokenParsingException;
import com.maezia.tdr.domain.po.ClientDetails;
import com.maezia.tdr.service.AccessTokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@Slf4j
@Component
public class GlobalAuthFilter extends OncePerRequestFilter {

    private static String CONTENT_TYPE_VALUE = MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8";

    @Autowired
    private IgnorePathProperties ignorePathProperties;

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Autowired
    private AccessTokenService accessTokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
        try {
            String requestURI = request.getRequestURI();
            if(!checkAllowPath(requestURI)){
                String tokenValue = request.getHeader(Header.AUTHORIZATION.getValue());
                if(StringUtils.isEmpty(tokenValue)){
                    handlerMessage(response, Result.error(StatusCodeEnum.TOKEN_NOT_NULL.getCode(), StatusCodeEnum.TOKEN_NOT_NULL.getMessage()));
                    return;
                }

                if(!tokenValue.startsWith(CommonConstant.TOKEN_PREFIX_VALUE)){
                    handlerMessage(response, Result.error(StatusCodeEnum.INVALID_TOKEN_FORMAT.getCode(), StatusCodeEnum.INVALID_TOKEN_FORMAT.getMessage()));
                    return;
                }

                String token = tokenValue.substring(CommonConstant.TOKEN_PREFIX_VALUE.length());
                boolean isExpired = jwtTokenProvider.isExpired(token);
                if(isExpired){
                    handlerMessage(response, Result.error(StatusCodeEnum.TOKEN_EXPIRED.getCode(), StatusCodeEnum.TOKEN_EXPIRED.getMessage()));
                    return;
                }

                ClientDetails clientDetails = jwtTokenProvider.getUserInfo(token, ClientDetails.class);
                String bufferToken = accessTokenService.getAccessToken(clientDetails.getClientId());
                if(StringUtils.isEmpty(bufferToken)){
                    handlerMessage(response, Result.error(StatusCodeEnum.TOKEN_EXPIRED.getCode(), StatusCodeEnum.TOKEN_EXPIRED.getMessage()));
                    return;
                }

                if(!token.equals(bufferToken)){
                    handlerMessage(response, Result.error(StatusCodeEnum.UNAUTHORIZED_TOKEN.getCode(), StatusCodeEnum.UNAUTHORIZED_TOKEN.getMessage()));
                    return;
                }
            }
            filterChain.doFilter(request, response);
        }catch (TokenParsingException e){
            handlerMessage(response, Result.error(StatusCodeEnum.TOKEN_EXPIRED.getCode(), StatusCodeEnum.TOKEN_EXPIRED.getMessage()));
        }catch (Exception e){
            handlerMessage(response, Result.error(StatusCodeEnum.SYSTEM_ERROR.getCode(), StatusCodeEnum.SYSTEM_ERROR.getMessage()));
        }
    }

    private boolean checkAllowPath(String reqPath){
        List<String> allowPaths = ignorePathProperties.getAllowPaths();
        if(CollectionUtils.isEmpty(allowPaths)){
            return false;
        }

        for(String allowPath : allowPaths){
            String prefixPath = allowPath.replaceAll("\\/\\*\\*","");
            if(reqPath.startsWith(prefixPath)){
                return true;
            }
        }
        return false;
    }

    private void handlerMessage(HttpServletResponse response, Result result) {
        try {
            response.setContentType(CONTENT_TYPE_VALUE);
            response.getWriter().write(JSONObject.toJSONString(result));
        } catch (IOException e) {
            log.error("IOException:", e);
        }
    }
}
